package services

import (
	"gitee.com/zhoulvvv/my_go_gateway/dao"
	"gitee.com/zhoulvvv/my_go_gateway/dto"
	"gitee.com/zhoulvvv/my_go_gateway/public"
	"github.com/e421083458/golang_common/lib"
	"github.com/gin-gonic/gin"
	"github.com/pkg/errors"
	"log"
	"net/http/httptest"
	"strings"
	"sync"
)

var ServiceManagerServiceHandler *ServiceManagerService

func init() {
	ServiceManagerServiceHandler = NewServiceManager()
}

type ServiceManagerService struct {
	ServiceMap   map[string]*dao.ServiceDetail // key是服务名称
	ServiceSlice []*dao.ServiceDetail
	Locker       sync.RWMutex
	init         sync.Once
	err          error
}

func NewServiceManager() *ServiceManagerService {
	return &ServiceManagerService{
		ServiceMap:   map[string]*dao.ServiceDetail{},
		ServiceSlice: []*dao.ServiceDetail{},
		Locker:       sync.RWMutex{},
		init:         sync.Once{},
	}
}

// 把数据库存储的服务信息都加载到内存，只加载一次
func (s *ServiceManagerService) LoadOnce() error {
	// 只执行一次
	s.init.Do(func() {
		tx, err := lib.GetGormPool("default")
		if err != nil {
			s.err = errors.New("获取数据库连接池失败")
			return
		}
		serviceInfo := &dao.ServiceInfo{}
		c, _ := gin.CreateTestContext(httptest.NewRecorder())
		param := &dto.ServiceListInput{PageNo: 1, PageSize: 999999}
		offset := (param.PageNo - 1) * param.PageSize
		param.PageNo = offset
		serviceInfoList, _, err := serviceInfo.PageList(c, tx, param)
		if err != nil {
			s.err = errors.New("分页查询服务列表出现错误")
			return
		}
		serviceDetailServices, err := NewServiceDetailServices()
		if err != nil {
			s.err = errors.New("ServiceDetailServices 创建失败：" + err.Error())
			return
		}
		s.Locker.Lock()
		defer s.Locker.Unlock()
		for _, serviceInfo := range serviceInfoList {
			// 获取服务的具体信息
			serviceDetailModel, err := serviceDetailServices.GetServiceDetail(c, &serviceInfo)
			if err != nil {
				s.err = errors.New("获取serviceDetail信息失败：" + err.Error())
				return
			}
			s.ServiceMap[serviceInfo.ServiceName] = serviceDetailModel
			s.ServiceSlice = append(s.ServiceSlice, serviceDetailModel)
		}
	})
	return s.err
}

// 获取TCP服务信息
func (s *ServiceManagerService) GetTcpServiceList(c *gin.Context) []*dao.ServiceDetail {
	list := []*dao.ServiceDetail{}
	for _, item := range s.ServiceSlice {
		tempItem := item
		if tempItem.Info.LoadType == public.LoadTypeTCP {
			list = append(list, tempItem)
		}
	}
	return list
}

// 获取GRPC服务信息
func (s *ServiceManagerService) GetGrpcServiceList(c *gin.Context) []*dao.ServiceDetail {
	list := []*dao.ServiceDetail{}
	for _, item := range s.ServiceSlice {
		tempItem := item
		if tempItem.Info.LoadType == public.LoadTypeGRPC {
			list = append(list, tempItem)
		}
	}
	return list
}

// HTTP 服务匹配
func (s *ServiceManagerService) HTTPAccessMode(c *gin.Context) (*dao.ServiceDetail, error) {
	// 1、前缀匹配 /abc
	// 2、域名匹配 www.test.com
	host := c.Request.Host                  // www.test.com:8080 Host也会拿到端口信息
	host = host[0:strings.Index(host, ":")] // 截取端口
	path := c.Request.URL.Path              // /abc 拿取前缀，如果路径有参数，不会拿到参数
	log.Printf("请求域名：%s 请求地址：%s", host, path)
	for _, serviceItem := range s.ServiceSlice {
		// 判断服务是不是HTTP类型，不是直接跳过
		if serviceItem.Info.LoadType != public.LoadTypeHTTP {
			continue
		}
		// 域名匹配方式
		if serviceItem.HTTPRule.RuleType == public.HTTPRuleTypeDomain {
			// 匹配域名
			if serviceItem.HTTPRule.Rule == host {
				return serviceItem, nil
			}
		}
		// 前缀匹配方式
		if serviceItem.HTTPRule.RuleType == public.HTTPRuleTypePrefix {
			// 匹配前缀
			if strings.HasPrefix(path, serviceItem.HTTPRule.Rule) {
				return serviceItem, nil
			}
		}
	}
	return nil, errors.New("【请求没有匹配到服务】request not match service")
}
